// vue2响应式原理
// 对象类型：通过Object.defineProperty()对属性的读取，修改进行拦截（数据劫持）。
// 数组类型：通过重写更新数组的一系列方法来实现拦截。（对数组的变更方法进行了包裹）
// Object.defineProperty(data, 'count', {
//     get() {},
//     set() {}
// })

// 缺点
// （1）vue2 对象中不存在的属性不能被拦截， Object.defineProperty()只能劫持对象
// （2）数组改变length属性不会驱动视图更新，虽然长度被改变了。
// （3）当对象是一个嵌套的对象的时候，需要使用递归进行深度监听，vue2当中 递归是默认执行的
// （4）vue2中无法监听到对象的增删，所以vue2还补充了Vue.set()，Vue.set()，Vue.set()，Vue.delete()等其他方法进行弥补。
// （5）对象新增的属性没有响应式 数组的部分操作没有响应式。数组的一些方法会改变数组，在vue2中被重写。

// vue2.0 响应式原理--数据变了  可以更新视图
function type(data) {
    return Object.prototype.toString.call(data).slice(8, -1) // 'Number'
}

let oldArrayPrototype = Array.prototype
let proto = Object.create(oldArrayPrototype) // 继承

// pushxxxx  函数劫持， 把函数重写 
Array.from(['push', 'shift', 'unshift', 'pop']).forEach(method => {
    proto[method] = function () {
        oldArrayPrototype[method].call(this, ...arguments)
        updateView()
    }
})

// observer函数专门用于劫持数据
function observer(target) {
    if (typeof target !== 'object' || typeof target == null) { // 崩
        return target
    }

    if (Array.isArray(target)) {
        // target.__proto__ = proto
        Object.setPrototypeOf(target, proto)
    }

    for (let key in target) {
        defineReactive(target, key, target[key])
    }
}

// 响应式
function defineReactive(target, key, value) {
    observer(value)//这个递归是默认执行的
    // Object.defineProperty () 可以重新定义属性 给属性安插 getter setter 方法
    Object.defineProperty(target, key, {
        get() {
            return value
        },
        set(newVal) {
            if (newVal !== value) {
                value = newVal
                updateView()
            }
        }
    })
}


function updateView() {
    console.log('更新视图');
}

let data = {
    name: 'hunny',
    age: {
        n: 19
    },
    hobby: ['reading']
}
observer(data)

// （1）当改变属性是基本数据类型的时，不触发更新视图
data.name = 'hunny'
// （2）但改变属性是对象类型的值时，触发更新视图
data.age.n = 101
data.age = {
    n: 102
}
// （3）当属性不存在时，不触发更新视图
data.sex = 'boy'

// （4）当改变数组类型，触发更新视图
Array.prototype.push = function () { }
data.hobby.push('singing')

// （5）当改变数组原本的长度，数组长度会改变，会放两个empty,占位符，但是不触发更新视图
data.hobby.length = 5
console.log(data.hobby.length); //5，实际上是2